/*
    Project:        Home Power Control
    File:           HPCSensor.h
    Author:         christian@karunamusic.at
    Date:           11.2014
    Description:    Baseclass for all sensors.
 */

#ifndef HPCSENSOR_H
#define HPCSENSOR_H

#include <boost/thread.hpp>
#include <boost/signals2.hpp>
#include "HPCSensorValue.h"

class HPCUnit;

/** \brief Baseclass for all sensors.
 * Only HPCUnits can connect to the sensors OnChange event in an 1:1 relation.
 *
 */
class HPCSensor
{
public:
    friend class HPCUnit;

    /** \brief Typedefinition of the callbackmethod for changed values.
     *
     * \param sender pointer to the sender of the notification.
     * \param val HPCValue - changed value of the sensor.
     * \return nothing.
     *
     */
    typedef boost::signals2::signal<void(HPCSensor *,const HPCSensorValue &)> TSignalChange;

    /** \brief Typedefinition of the callbackmethod for alarmed values.
     *
     * \param sender pointer to the sender of the notification.
     * \param val HPCSensorValue - alarmed value of the sensor.
     * \param max if true alarm is raised because of val >= max, otherwise val <= min.
     * \param ndx index of the fired alarm-value in the Min/Max-vector of HPCSensorValue.
     * \return nothing.
     *
     */
    typedef boost::signals2::signal<void(HPCSensor *,const HPCSensorValue &, bool, unsigned int)> TSignalAlarm;

    /**< Type of the sensor. */
    enum class SensorType
    {
        HPCButton,
        HPCLight,
        HPCAnemometer,
        HPCRainGauge,
        HPCWindVane,
        HPCHumidity,
        HPCTemperature
    };

    /** \brief Constructor
     *
     * \param id database id of the sensor.
     * \param pin GPIO pin number.
     *
     */
    HPCSensor( unsigned long id, unsigned int pin );

    /** \brief Destructor.
     */
    virtual ~HPCSensor();

    /** \brief Get database id.
     *
     * \return unsigned long
     *
     */
    inline unsigned long ID() const
        { return m_ID; }

    /** \brief Get GPIO pin number.
     *
     * \return unsigned int
     *
     */
    inline unsigned int Pin() const
        { return m_Pin; }

    /** \brief Set sensors OnChange behavior.
     * true:  fire OnChange event each interval.
     * false: fire OnChange event only if value has vhanged.
     *
     * \param on Fire always.
     * \return the object.
     *
     */
    virtual HPCSensor& FireAlways( bool on );

    /** \brief Abstract method to get the type of the sensor-class.
     *
     * \return SensorType Type of the sensor (from enum SensorType).
     *
     */
    virtual SensorType Type() const = 0;

    /** \brief Get the name of the sensor-class.
     *
     * \return the classname as string.
     *
     */
    std::string Name() const
        { return m_Name; }

    /** \brief Set the name of the sensor-class.
     *
     * \return the class.
     *
     */
    HPCSensor& Name( const std::string name )
        { m_Name = name; return *this; }

    /** \brief Get the measuring unit.
     *
     * \return the measuring unit as string.
     *
     */
    std::string MeasureUnit() const
        { return m_MeasureUnit; }

    /** \brief Set the measuring unit.
     *
     * \return the class.
     *
     */
    HPCSensor& MeasureUnit( const std::string munit )
        { m_MeasureUnit = munit; return *this; }

    /** \brief Get class with content as printable string.
     *
     * \return string
     *
     */
    virtual std::string asString() const;

    /** \brief Add upper and lower alarm to the SensorValue with the given id.
     * Can be called more than once to add alarms.
     *
     * \param Min The min-value as string or NULL if unused.
     * \param Max The max-value as string or NULL if unused.
     *
     */
    void addAlarm( const char* Min, const char* Max );

protected:
    /** \brief Abstract method to get a value from the sensor.
     *  Has to be implemented in derived classes!
     *
     */
    virtual void Measure() = 0;

    unsigned long m_ID; /**< Database id. */
    const unsigned int m_Pin; /**< GPIO pin number. */
    std::string m_Name; /**< Name of the sensor. */
    std::string m_MeasureUnit; /**< The measuring unit of the sensor. */
    bool m_FireAlways; /**< if true, fire OnChange each interval (even if unchanged) */
    HPCSensorValue m_Value; /**< the measured value of the sensor. */
    HPCValue m_Old; /**< save the measured value to detect changes. */
    TSignalChange m_OnChange; /**< the callback-method for changed values. */
    TSignalAlarm  m_OnAlarm;  /**< the callback-method for alarmed values. */

private:
    boost::signals2::connection m_OnChangeHandle; /**< handle of the connected OnChange callback-method. */
    boost::signals2::connection m_OnAlarmHandle;  /**< handle of the connected OnAlarm  callback-method. */

    /** \brief Set eventhandler for changed values.
     *
     * \param subscriber method called if the value of the sensor has changed.
     *
     */
    void OnChange( const TSignalChange::slot_type &subscriber );

    /** \brief Set eventhandler for alarmed values.
     *
     * \param subscriber method called if the value of the sensor is out of limits.
     *
     */
    void OnAlarm( const TSignalAlarm::slot_type &subscriber );
};

#endif // HPCSENSOR_H
